Skip to content

feat(tabs): rename the current tab from a shortcut and the palette - #1981

Merged
devswha merged 6 commits into
mainfrom
feat/rename-tab-shortcut
Oct 26, 2026
Merged

devswha merged 6 commits into
mainfrom
feat/rename-tab-shortcut

Conversation

@devswha

@devswha devswha commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Change

Fixes #1141.

Rename tab is now a command palette action and a rebindable shortcut, so a tab can be renamed without the mouse. Both open the same inline name field as the tab menu's Rename, on the selected pane's tab: Enter saves and Escape cancels, as before.

  • Shortcut. rename-tab joins SHORTCUTS and CUSTOM_SHORTCUT_IDS with no default key, like New tab and the splits. The free Mod+Shift letters are few, and the issue's suggestions are taken (Mod+Shift+E focuses the input, Mod+Shift+R is Chrome's hard reload). Bind it in Settings → Shortcuts and the palette shows the chord. A held chord does not repeat.
  • Palette. A Rename tab row (lucide Pencil), found by "edit tab name title" too. It is left out where the strip is not drawn (a lone tab on a cramped phone screen, loneTabHidden in lib/paneCanvas.ts, now shared with TabStrip) or while the selected pane is not in the roster yet.
  • One-shot request. App holds a { machineId, paneId } request (lib/useRenameRequest.ts) that only the active half's strip for that pane receives. The strip opens the field and clears the request at once. The request is also dropped when the selection changes, so a request no strip took up (a crashed half) never fires later, and returning to the pane never reopens the field. A second press while the field is open keeps what was typed.
  • "Rename tab" moves from the lazy terminal i18n chunk to the base strings, since the palette now uses it. DESIGN.md's tab menu entry and docs/guide.md mention the new entry points, and a changelog fragment is included.

Design reference: herdr's own TUI has rename_tab (default prefix+shift+t), and VS Code and Zed offer Rename tab as palette commands. This follows herdr's action, bound to a web chord the browser leaves alone.

Validation

  • bun scripts/shortcut-exact-modifiers-demo-regression.ts: PASS. On the demo build it records Mod+Shift+Y for Rename tab, which opens and focuses the name field; Escape keeps the name; leaving the pane and coming back does not reopen the field; a second press keeps typed text; Enter renames, and the demo's /api/session snapshot carries the new name; the palette action opens and focuses the field. Negative check: without the "keep typed text" guard in TabStrip, the case fails (the field is reset and never closes).
  • Browser, demo build, Chromium at 1440×900 and 390×844 (touch): the shortcut and the palette row open the focused name field in the tab strip, with no page errors.
  • bun test src/lib/shortcuts.test.ts src/conventions.test.ts: 39 pass. tsc --noEmit: clean.
  • bun run check fast on be1f8c8: workflow syntax, generated types and themes, typecheck, build, and unit tests (7188 pass, 0 fail) all pass.
  • Reviews: omo-native-code-reviewer (approve; its MEDIUM about an unmounted strip and its LOW about a second press are fixed in 30705c3). Codex Sol review did not run (host stall); the merge pass reviews.

@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 34 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 7ab4bde and be1f8c8.

Files selected for processing: 24
  • DESIGN.md
  • changelog.d/rename-tab-shortcut.md
  • docs/guide.md
  • scripts/session-history-fixture.tsx
  • scripts/shortcut-exact-modifiers-demo-regression.ts
  • src/App.tsx
  • src/components/CommandPalette.tsx
  • src/components/TabStrip.tsx
  • src/lib/actions.ts
  • src/lib/i18n.ja.ts
  • src/lib/i18n.ko.ts
  • src/lib/i18n.zh.ts
  • src/lib/i18n/settings.ja.ts
  • src/lib/i18n/settings.ko.ts
  • src/lib/i18n/settings.zh.ts
  • src/lib/i18n/terminal.ja.ts
  • src/lib/i18n/terminal.ko.ts
  • src/lib/i18n/terminal.zh.ts
  • src/lib/paletteSettings.ts
  • src/lib/paneCanvas.ts
  • src/lib/shortcutBindings.ts
  • src/lib/shortcuts.test.ts
  • src/lib/shortcuts.ts
  • src/lib/useRenameRequest.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devswha

devswha commented Oct 26, 2026

Copy link
Copy Markdown
Owner Author

Merge pass review report. Overall: PASSED. This change is client only. The Astra gate (omo-native-gate-reviewer, gpt-6-astra) approved be1f8c8.

What the gate checked:

  • Rename tab is now a palette action and a rebindable shortcut (rename-tab, no default key). It opens the same inline field as the tab menu's Rename, on the selected pane's tab, and the right tab is targeted inside a split.
  • Native herdr focus is unchanged, so the AGENTS bridge trap holds.
  • Palette keys work on desktop and on mobile.
  • scripts/shortcut-exact-modifiers-demo-regression.ts passes, as do 61 targeted tests.
  • With the registration removed, a negative control fails as it should.

Not exercised: scrolled-out tabs, a cramped lone tab, and duplicate ids across remote machines were checked by reading the code. CodeRabbit was rate-limited.

git merge-tree is clean against main, and against every PR it shares files with (#1969, #1973, #1975, #1977, #1979). Locally shortcuts.test.ts passes 31/31 and typecheck is clean.

@devswha
devswha merged commit 72e3060 into main Oct 26, 2026
11 checks passed
@devswha
devswha deleted the feat/rename-tab-shortcut branch October 26, 2026 10:57
devswha added a commit that referenced this pull request Oct 31, 2026
Closes #2019.

## Change
A tab herdr still names by its number ("Tab 2") shows a few words of the
first message its agent was given: about 24 columns, where a wide
character counts as two, cut at a word's end with `…`. The tab's hover
stays the pane's title. Tabs of the same agent can be told apart without
opening each one.

- **An Ovis display label, never written to herdr.** Labels are kept in
the shared preferences as `tab_labels`, keyed
`<paneStorageId>|<terminal_id>`. herdr reuses pane ids after a restart
but never a terminal id, so a new pane never shows an old pane's words.
On the server they live in their own `tab-labels.json`, like
`muted-panes.json`, so an older release that rewrites `preferences.json`
erases nothing. herdr's tab label, its TUI and the workspace name are
untouched.
- **Reading never writes.** A label read from a transcript shows on that
page only. A user's action shares it with every browser: the New tab
dialog's Start, or a message delivered from Chat to an agent's pane.
Opening, reading or watching a page never sends `PUT /api/preferences`.
A watching device shows the labels and learns none.
- **Privacy.** Only the short label is stored (at most 64 UTF-16 units),
never the message. A label leaves once a browser saw its pane and then
saw it gone; that drop goes out with the next change someone makes.
Turning the shared setting off deletes every stored label, and none is
loaded or kept while it stays off. The setting's description says what
is kept.
- **The labels file never blocks other preferences.** A
`tab-labels.json` that can't be read or written is logged, and the
labels are left out of that save. Labels never count against the 64 KB
size of a settings, pins or mutes save.
- **Never covers a given name.** Only herdr's own numbered name counts
as the default. A name from Rename tab (#1981), the New tab dialog's
Name or a script is kept, even one that is just an agent's name.
- **Where the words come from.** The first line that names something is
used: a slash command, a path or a code fence is skipped. Sources, for
every agent whose first prompt Ovis knows:
  - the New tab dialog's first message;
- a conversation page read from its beginning (`cursor === null`), for
every transcript-backed agent;
- a Chat send to a pane with no transcript, once it is delivered (not
one held, queued or answering a question; never a shell pane).
- **Settings → Appearance → Name tabs from the first message**: shared,
on by default.
- The palette footer names a tab the way the strip does.

Design follows the Codex app, which titles a thread from its first
prompt, rather than asking the model for a title (a model call and a
wait). DESIGN.md has the entry.

### Contract
`GET/PUT /api/preferences`:
- The document gains `tab_labels: { "<paneStorageId>|<terminal_id>":
label }`. It is absent while none is kept, so an old document reads
exactly as before. There are at most 64 labels (the oldest leave first),
and each is one line of at most 64 units.
- A PUT adds labels with `tab_labels` (a key already held keeps its
label) and drops keys with `tab_labels_removed`. The server merges both
per key, so two browsers saving at once lose neither's labels.
- While `autoTabNames` is false the server keeps none.
- A browser that gets an answer without the labels it sent treats the
server as one that keeps none and sends no more, until its event stream
reopens. Old clients ignore the fields.

## Validation
- `bun run check fast`: ok at 2c5e318 (unit tests 10055 pass, 0 fail).
- CI's `pane-mute-demo-regression` and
`background-agent-detail-demo-regression` were reproduced locally and
pass now (exit 0).
- Failing-first: each new test fails without its change, either on the
earlier commit or under a mutation. They cover:
  - a pane id reused after a restart;
  - the purge when the setting is turned off;
  - concurrent saves;
  - a stale roster;
  - an answer landing after the pane closed;
  - reading never writing;
  - a removal alone sending nothing;
  - only agent panes learning from a send;
- an unreadable or unwritable labels file, and the setting turned off
over one;
  - labels left out for size;
  - a failed delete while the setting is off.
- Browser QA on an isolated herdr test session (`ovis-test-feat2019`,
fresh state dir, port 0, built `dist/`), with two browser contexts, all
PASS with no page errors:
- A (Chat) showed "Fix the login redirect…"; the hover stayed the pane
title.
- Reading the transcript shared nothing: B still showed "Tab 1", and no
`tab-labels.json` existed.
  - After a Chat send in A, B showed the label without a reload.
- The file held only the short label under `<pane>|<terminal>`; herdr's
tab label stayed "1" and the workspace name was unchanged.
- A rename to "auth work" and to "codex" was kept; a rename back to "1"
brought the label back.
- Turning the setting off showed "Tab 1" in both browsers and emptied
the file.
  - Screenshots at desktop 1280 and phone 390, Codex dark and light.
- Codex Terra reviews (gpt-6.1-terra, xhigh; never Astra) over every
round. Each finding was fixed, except one accepted edge, explained in
the PR comments: old labels can return after a restart only if every
delete of the file fails while the setting is off, through the save that
turns it back on.
- Skipped: `check full` (CI runs Integration and browser).

This changes the `/api/preferences` HTTP contract, so it waits for a
real review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keyboard shortcut to rename the current tab

1 participant